'''此模块用于实现对一组Excel数据基本信息进行批量读取，分析这一组Excel数据的列结构，通过
配置列结构将这一组数据合并为一个数据表。

1.读取含有一组Excel路径数据的DataFrame表中每个Excel文件的基本信息。  
2.通过基本信息对一组Excel文件中的列名结构以及列名进行分析。  
3.获取一组Excel文件基本信息列表中的所有文件列名,并进行一致性分析。然后把结果输出，以便配
置使用的列。
4.获取一组Excel文件中列名配置后的结果。
5.将结构一致或近似的一组Excel文件合并为一个表数据。
'''

import logging
import os.path
import pandas as pd
from datatools import ExcelReader,XlsxWriterFormat
from datatools.excel.excelwalk import CONFIG_DIR,create_config_dir

mylog = logging.getLogger()

def get_excels_columns(excel_infos):
    '''获取文件基本信息列表中的所有文件列标题,并进行一致性分析。
    excel_infos:由函数analysis_excels返回的文件基本结构信息表。
    返回：（columns_detail_df，columns_count_df）
      @columns_detail_df：描述列名对应结构内容的详细信息表。
      @columns_count_df：所有列名统计分析表。
    '''
    #获取所有文件中列名表
    columns_detail_df = pd.DataFrame(columns=['文件编号','文件路径','Sheet表名','列名','是否使用','使用别名','列名的别名','列数据类型'])
    for file_item in excel_infos:
        temp_list = []
        file_id = file_item['file_id']
        file_path = file_item['file_path']
        file_sheet = file_item['sheetname']
        file_columns = file_item['columns']
        file_columns_type = file_item['columns_type']
        for index,column_item in enumerate(file_columns):
            temp_list.append([file_id,file_path,file_sheet,column_item,'是','否','',file_columns_type[index]])
        temp_df = pd.DataFrame(temp_list,columns=['文件编号','文件路径','Sheet表名','列名','是否使用','使用别名','列名的别名','列数据类型'])
        columns_detail_df = pd.concat([columns_detail_df,temp_df],ignore_index=True)
    
    #不要在只有一个分组依据的时候使用列表给出，否则将会产生FutureWarning
    groups = columns_detail_df.groupby('列名')
    
    #分析所有列名的出现次数，确定需要映射的列
    columns_count = []
    for gp_id,gp_data in groups:
        columns_count.append([gp_id,gp_data['列名'].count()])
    columns_count_df = pd.DataFrame(columns_count,columns=['列名','出现次数'])
    
    #查找需要映射的列，标记
    need_map = columns_count_df.loc[columns_count_df['出现次数']<len(excel_infos)]
    if not need_map.empty:
        for index,line in need_map.iterrows():
            columns_detail_df.loc[columns_detail_df['列名']==line['列名'],['使用别名']] = '是'
                            
    return columns_detail_df,columns_count_df

def out_excels_columns_config(excel_infos,path="当前处理Excel文件标题配置.xlsx"):
    '''获取一组Excel文件基本信息列表中的所有文件列标题,并进行一致性分析。然后把结果输出，以便配置使用的列。
    excel_infos:由函数analysis_excels返回的文件基本结构信息表。
    '''
    create_config_dir()
    path = os.path.join(CONFIG_DIR,path)
    files_column_df,columns_count_df = get_excels_columns(excel_infos)
    with XlsxWriterFormat(path) as xw:
        xw.to_excel(files_column_df,'列使用配置',title='列使用配置')
        xw.to_excel(columns_count_df,'列名分析',title='列名分析')
        
def get_exels_columns_config(path="当前处理Excel文件标题配置.xlsx"):
    '''获取由out_excels_columns_config产生的excel文件经过配置后的数据。
    返回：excels_used_columns
    excels_used_columns：从列配置文件中读取到配置后所用列信息的数据表。
    '''
    path = os.path.join(CONFIG_DIR,path)
    data = pd.read_excel(path,sheet_name='列使用配置',header=1)
    excels_used_columns = data.loc[data['是否使用'].isin(['是','Y',True,'TRUE','True'])]
    return excels_used_columns    

def concat_same_excel(excel_infos,excels_used_columns):
    '''将结构一致或近似的一组Excel文件合并为一个表数据。
    1.使用get_all_excel_struct获取指定目录下所有Excel文件的结构。
    2.使用out_excel_path_struct输出Excel文件结构为xlsx文件，进行配置。
    3.使用get_excel_path_struct获取配置后实际使用的Excel文件结构。
    4.使用analysis_excels获取指定文件结构的Execel文件信息。
    5.使用out_excels_columns_config将这一组文件中的列名结构以及列名分析输出为Excel文件，用于手动处理。
        （1）.使用get_excels_columns获取这一组文件中的列名结构以及列名分析。
        （2）.使用xlsxwriterformat将结果写入“当前处理Excel文件标题配置.xlsx”中，可指定写入路径。
    6.手动编辑“当前处理Excel文件标题配置.xlsx”文件，配置使用到的列及列名别名，列名对应列的数据类型。
    7.调用get_exels_columns_config获取配置后所有使用到的列的详细信息。
    5.调用此文件组合这一组Excel数据。
    '''
    
    #计算新生成的列
    all_new_columns = ['文件编号']
    for index,line in excels_used_columns.iterrows():
        column_name = line['列名']
        need_map = line['使用别名']
        map_column = line['列名的别名']
        if need_map in ['是','Y',True,'TRUE','True']:
            if not pd.isna(map_column):
                all_new_columns.append(map_column)
                continue
        all_new_columns.append(column_name)
    all_new_columns = pd.unique(all_new_columns)
    all_df = pd.DataFrame(columns=all_new_columns)
    
    #迭代生成每个要读取的表
    #使用通过analysis_excels分析被处理表得到的excel文件基本信息提取所有用到的文件路径及Sheet表名。如果使用使用
    #excels_used_columns获取用到的所有Excel文件路径及Sheet表名，则需要进行运算。
    for file_item in excel_infos:
        temp_list = []
        type_list = []
        new_column = []
        file_id = file_item['file_id']                   #要处理的文件编号
        file_path = file_item['file_path']               #要处理的文件路径
        file_sheet = file_item['sheetname']              #要处理的文件Sheet名称
        file_columns = file_item['columns']              #指定的文件路径及Sheet名称下的数据表的列名列表
        file_columns_type = file_item['columns_type']    #列名列表对应列的数据类型列表
        header_index = file_item['columns_index']        #列名所在位置
        
        #生成读取文件所需列标题名
        #获取用到的原始的表的列名，以文件编号为索引取列名列表，获取配置后使用的列名
        orignal_columns = excels_used_columns.loc[excels_used_columns['文件编号']==file_id]['列名'].tolist()
        #获取相同文件中，需要起别名的列名
        mapped_df = excels_used_columns.loc[(excels_used_columns['文件编号']==file_id)& (excels_used_columns['使用别名'].isin(['是','Y',True,'TRUE','True']))]
        #数据类型可能也会发生改变，获取数据类型
        columns_type_config = excels_used_columns.loc[excels_used_columns['文件编号']==file_id]
        #考虑到配置后的列名位置顺序可能发生了变化，使用原始信息中的列名
        #excel_infos中的列名和列类型通过位置对应在一起
        for index,column_item in enumerate(file_columns):
            #判断是否用到这个列名
            if column_item in orignal_columns:
                #用到了此列名
                temp_list.append(column_item)
                #type_change = columns_type_config['列数据类型'].values[0]
                type_change = columns_type_config.loc[columns_type_config['列名']==column_item,['列数据类型']]
                if not type_change.empty:
                    new_type = type_change['列数据类型'].tolist()[0]
                    if new_type in ['object','str','int','float','int64','datetime64[ns]','float64']:
                        type_list.append(new_type)
                else:
                    type_list.append(file_columns_type[index])
                if not mapped_df.empty:
                    mapped_columns = mapped_df.loc[mapped_df['列名'] == column_item]
                    if not mapped_columns.empty:
                        if not pd.isna(mapped_columns['列名的别名'].values[0]):
                            new_column.append(mapped_columns['列名的别名'].values[0])
                            continue
                new_column.append(column_item)
        data_type = {key:value for key,value in zip(temp_list,type_list)}

        data = pd.read_excel(file_path,sheet_name=file_sheet,header=header_index,usecols=temp_list,dtype=data_type)
        order_column_data = data.loc[:,temp_list]
        order_column_data.columns = new_column
        order_column_data.insert(0,'文件编号',file_id)
        all_df = pd.concat([all_df,order_column_data],ignore_index=True)
    return all_df